- 使用 top 命令来查看各个进程的使用情况
常用交互命令 | 解释 |
---|---|
q | 退出程序 |
I | 切换显示平均负载和启动时间的信息 |
P | 根据CPU使用百分比大小进行排序 |
M | 根据驻留内存大小进行排序 |
k | 终止一个进程,系统提示输入 PID |
i | 忽略闲置和僵死的进程 |
- 查看 CPU 的个数与核心数
# 查看物理 CPU 的个数 cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l # 查看每个 CPU 的核心数 cat /proc/cpuinfo | grep "physical id" | grep "0" | wc -l
1. 解压 .tar 文件 $ tar -xf *.tar 2. 解压 .tar.gz 文件 $ tar -xzf *.tar.gz 3. 解压 .tar.xz 文件 $ xz -d *.tar.xz $ tar -xf *.tar 4. 解压 .tar.bz2 文件 $ tar -jxvf *.tar.bz2 - 如果tar不支持j选项,就用下面方式解压 $ bzip2 -d *.tar.bz2 $ tar -xvf *.tar.bz2 5. 解压 .tar.Z 文件 $ tar -xZf *.tar.Z 6. 解压 .zip 文件 $ unzip *.zip 7. 解压到指定路径 $ tar -xzvf *.tar.gz -C ~/dic/ $ unzip *.zip -d ~/dic/
安装
tar -xzf nginx-${version}.tar.gz cd nginx-${version} yum install -y pcre-devel openssl-devel ./configure make make install
nginx 默认安装路径
/usr/local/nginx
启动
${nginx_path}/sbin/nginx
启动指定配置文件
${nginx_path}/sbin/nginx -c ${conf_path}
重启
${nginx_path}/sbin/nginx -s reload
停止
${nginx_path}/sbin/nginx -s stop
在 http-server-location / 下填写反向代理配置
proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- 修改 Nginx 的配置文件
# ...
http {
# ...
server {
listen 80;
server_name www.saisimon.net;
# 重定向到 https
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
# ...
}
# ...
}
# ...
- 测试配置文件是否正确,重启 Nginx 服务
$ sudo service nginx configtest $ sudo service nginx restart
- 安装脚本
# 直接通过 url 安装 $ curl https://get.acme.sh | sh # 或者从 git 中安装 $ git clone https://github.com/Neilpang/acme.sh.git $ cd ./acme.sh $ ./acme.sh --install
- 为域名申请 SSL 证书,以 www.saisimon.net 为例
# www.saisimon.net 需要申请 SSL 证书的域名地址 # /home/saisimon/public nginx 配置的 root 目录,acme.sh 会在 Nginx 根目录下生成验证文件,验证完成后自动删除验证文件 $ acme.sh --issue -d www.saisimon.net -w /home/saisimon/public Your cert is in /home/saisimon/.acme.sh/www.saisimon.net/www.saisimon.net.cer Your cert key is in /home/saisimon/.acme.sh/www.saisimon.net/www.saisimon.net.key The intermediate CA cert is in /home/saisimon/.acme.sh/www.saisimon.net/ca.cer And the full chain certs is there: /home/saisimon/.acme.sh/www.saisimon.net/fullchain.cer # 若提示 acme.sh 不存在,请重新加载当前用户环境 $ source ~/.bashrc
- 复制/安装 SSL 证书
# 指定 key 与 fullchain 的安装路径,配置重启 Nginx 的命令,使用 force-reload 命令来加载证书 acme.sh --installcert -d www.saisimon.net \ --keypath /home/saisimon/ssl/www.saisimon.net.key \ --fullchainpath /home/saisimon/ssl/www.saisimon.net.key.pem \ --reloadcmd "sudo service nginx force-reload"
- 配置 DH 密钥
# 指定 dh 保存路径 openssl dhparam -out /home/saisimon/ssl/dhparam.pem 2048
- 配置 Nginx 启用 SSL
# 请确保 Nginx 带有 http_ssl_module 的 module,不知道有什么 module,可以使用以下命令查看 $ sudo nginx -V # 如果没有 http_ssl_module,请带上 ssl module 重新编译安装 Nginx $ cd $NGINX_HOME # 记得带上 nginx -V 获得的原来的参数 $ ./configure --prefix=/usr/share/nginx ... --with-http_ssl_module $ make
#... http { #... # 使用的 SSL 协议版本 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 服务器密码优先 ssl_prefer_server_ciphers on; # 禁止使用不安全的加密算法 ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4'; #... server { listen 443 ssl; server_name www.saisimon.net; root /home/saisimon/public; ssl_certificate /home/saisimon/ssl/www.saisimon.net.key.pem; ssl_certificate_key /home/saisimon/ssl/www.saisimon.net.key; ssl_dhparam /home/saisimon/ssl/dhparam.pem; #... } #... }
# 测试 Nginx 配置文件是否正确 $ sudo nginx -t # 重启 Nginx 服务 $ sudo nginx -s reload
- 验证 SSL 是否生效
使用 SSL Labs 测试
- 证书有效期
Let`s Encrypt 的证书有效期为 90 天,acme.sh 脚本已经往 crontab 增加了一行每天执行的命令,当证书快过期时去自动更新证书内容
$ crontab -l 6 0 * * * "/home/saisimon/.acme.sh"/acme.sh --cron --home "/home/saisimon/.acme.sh" > /dev/null
- 通过 pip 安装 cheat
- 安装 python 和 pip $ yum install python-pip -y - 更新 pip $ pip install --upgrade pip - 安装 cheat $ pip install cheat
- 通过 github 下载源码安装 cheat
- 安装 python 和 pip $ yum install python-pip -y - 更新 pip $ pip install --upgrade pip - 安装编译工具 $ pip install docopt pygments appdirs - clone cheat 源码 $ git clone git@github.com:chrisallenlane/cheat.git - 进入源码目录 $ cd cheat - 编译安装 cheat $ python setup.py install
- 列出目前所有的别名设置
$ alias alias l.='ls -d .* --color=auto' alias ll='ls -l --color=auto' alias ls='ls --color=auto' alias vi='vim'
- 设置指令的别名(本次登录有效)
$ alias [别名]=[指令名称] $ alias la='ls -a' $ alias cp='cp -i'
- 每次登录自动设置,修改自己的配置文件
# 编辑配置文件 $ vim ~/.bash_profile # 配置别名 alias ..='cd ..' alias cp='cp -i' alias df='df -h' alias home='cd ~' alias l.='ls -d .* --color=auto' alias la='ls -a --color=auto' alias ll='ls -l --color=auto' alias ls='ls --color=auto' alias targz='tar -xzvf' alias vi='vim' # 重新加载配置文件 $ source .bash_profile
#!/bin/bash
# 引入加载存有 alias 的 profile
source ~/.bash_profile
# 设置别名在 shell 脚本中可用
shopt -s expand_aliases
# 运用 alias
...
#!/bin/bash
# x 表示 username 当前在线的个数
x=$(who | grep -c $username)
if [ $x -lt 1 ]
then
echo "$username not login"
else
echo "$username login"
# one sudo useradd -m -g sudo username # 创建名为 username 的新用户,创建默认 home 目录,指定用户组为 sudo # two sudo adduser --home /home/username username # 创建名为 username 的新用户,并指定 home 目录位置 sudo usermod -aG sudo username # 加入 sudo 组
#!/bin/bash
function searchFile() {
for file in `ls $1`
do
if [ -d $1"/"$file ];then
searchFile $1"/"$file
elif [ -f $1"/"$file ];then
echo $1"/"$file
fi
done
}
searchFile "/home"
# 客户端生成公钥与私钥 [client@localhost]$ ssh-keygen -t rsa # 设置生成的目录位置,设置私钥密码 # 默认在 ~/.ssh 目录下生成 id_rsa 和 id_rsa.pub # 服务端配置 ssh 配置文件 [server@localhost]$ vim /etc/ssh/sshd_config #使用成对的密钥系统进行登录 RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile %h/.ssh/authorized_keys #禁用密码登录 PasswordAuthentication no # 重新启动 ssh 服务 [server@localhost]$ service ssh restart # 客户端将公钥上传至服务端 [client@localhost]$ scp ~/.ssh/id_rsa.pub <sever-user>@<server-ip>:~ # 服务端添加客户端的公钥到 authorized_keys 中 [server@localhost]$ cat id_rsa.pub >> ~/.ssh/authorized_keys
# 1. Ctrl + z, bg [root@localhost]$ mvn -Djetty.port=8888 jetty:run > jetty.log 2>&1 # 按下 Ctrl + z 挂起到后台暂停运行 ^Z [1]+ Stopped mvn -Djetty.port=8888 jetty:run > jetty.log 2>&1 # bg 命令将挂起的进程放在后台 [root@localhost]$ bg [1]+ mvn -Djetty.port=8888 jetty:run > jetty.log 2>&1 # 2. setsid 命令使执行进程不属于接受 HUP 信号的终端的子进程 [root@localhost]$ setsid mvn -Djetty.port=8888 jetty:run > jetty.log 2>&1 # 3. & 将命令放入后台运行 [root@localhost]$ (mvn -Djetty.port=8888 jetty:run > jetty.log 2>&1 &)
# 防火墙开启作用域为 public,80 端口,并且永久生效 [root@localhost]$ firewall-cmd --zone=public --add-port=80/tcp --permanent # 重新启动防火墙 [root@localhost]$ systemctl restart firewalld.service
- 安装 GoAccess
# 安装 NCurses 依赖
$ sudo yum install ncurses-devel
# 到 Home 目录
$ cd ~
# 下载 GoAccess 安装包
$ wget http://tar.goaccess.io/goaccess-1.2.tar.gz
# 解压
$ tar -xzvf goaccess-1.2.tar.gz
$ cd goaccess-1.2/
# 检查依赖,配置参数,生成 Makefile 文件
$ sudo ./configure --enable-utf8 --enable-geoip=legacy
# 编译
$ sudo make
# 安装
$ sudo make install
- 分析 Nginx 日志
# 将分析结果输出到当前终端
$ goaccess access.log -c
# 将分析结果输出为 HTML 文件,-o 指定输出位置,--log-format 指定 Nginx 日志的格式
$ goaccess access.log -o report.html --log-format=COMBINED
- Crontab 命令参数说明
# 用法
$ crontab <选项> 参数
# 编辑当前用户的定时器任务
$ crontab -e
# 显示当前用户的定时器任务列表
$ crontab -l
# 清空当前用户的定时器任务
$ crontab -r
# 指定 someone 用户的定时器任务操作
$ crontab -u someone -e
# 指定 someone 用户的定时器任务
$ crontab -u someone /home/someone/cronfile
- Crontab 用户任务格式
# 任务格式: cron表达式 + 命令 # cron表达式: # .---------------- 分钟 (0 - 59) # | .------------- 小时 (0 - 23) # | | .---------- 天 (1 - 31) # | | | .------- 月 (1 - 12) # | | | | .---- 星期 (0 - 6), 0或者7代表星期日 # | | | | | # * * * * * command
- 一些常用的 cron 表达式
# 每一分钟执行一次 # 2018/01/23 00:00:00 # 2018/01/23 00:01:00 * * * * * * date > date.log # 每五分钟执行一次 # 2018/01/23 00:00:00 # 2018/01/23 00:05:00 0/5 * * * * * date > date.log # 每一小时执行一次 # 2018/01/23 00:00:00 # 2018/01/23 01:00:00 0/5 * * * * * date > date.log # 每天01:30执行一次 # 2018/01/23 01:30:00 # 2018/01/24 01:30:00 30 1 * * * * date > date.log # 每月12号01:30执行一次 # 2018/01/12 01:30:00 # 2018/02/12 01:30:00 30 1 12 * * * date > date.log # 每周三01:30执行一次 # 2018/01/24 01:30:00 # 2018/01/30 01:30:00 30 1 * * * 3 date > date.log
**使用 crontab 定时执行脚本时要注意当前的环境变量,避免出现在定时执行时命令不存在的问题,当定时任务执行有误时,而没有将错误重定向到其他位置,cron默认会通过邮件的形式将执行结果保存在 /var/mail/{user} 文件下**
- Win + R 运行 regedit
- 找到 计算机 > HKEY_CLASSES_ROOT > Folder > shell
- 右键shell 新建 -> 项 -> 命令提示符
- 右键命令提示符 新建 -> 项 -> command
- 点击(默认)项 输入 C:/Windows/System32/cmd.exe /k cd %1 然后点击确认
- 打开XShell 选择 文件 -> 属性
- 选择 终端 -> 键盘
- 选中 将 Alt 用作 Meta 键
- BACKSPACE 键序列 中选择 ASCII 127 确认
jpg | FF D8 FF | png | 89 50 4E 47 |
gif | 47 49 46 38 | bmp | 42 4D |
25 50 44 46 | xml | 3C 3F 78 6D 6C | |
docx/xlsx/pptx | 50 4B 03 04 14 00 06 00 08 00 | doc/xls/ppt | D0 CF 11 E0 |
zip | 50 4B 03 04 | rar | 52 61 72 21 |
avi | 41 56 49 20 | class | ca fe ba be |
exe | 4D 5A 90 00 03 | psd | 38 42 50 53 |
ECHO "Start QQ..."
CALL ${qq path}
ECHO "Start Chrome..."
START ${chrome path} ${url path}
ECHO "Start Xshell..."
START ${xshell path} ${session path}
ECHO "..."
- Win + R 运行 regedit
- 找到 计算机 > HKEY_CURRENT_USER > Console > %SystemRoot%_system32_cmd.exe
- 将 CodePage 的数值数据修改为十六进制 3a8 或十进制 936 (简体中文 GBK) 或者十六进制 fde9 或十进制 65001 (UTF-8)
REM 开启指定可执行程序函数,若已运行则提示,反之则运行该程序
REM %1 程序名 %2 可执行文件路径 %3、%4 程序参数
:openProgram
ECHO Open %1...
REM 判断指定程序名是否运行,名称忽略大小写
tasklist | find /i "%1.exe"
if %errorlevel%==0 (ECHO %1 already running...) else (START %2 %3 %4)
goto:eof
REM 调用函数,启动 Eclipse
call:openProgram Eclipse D:\eclipse\eclipse.exe
- 选择指定 information_schema 数据库
use information_schema;
- 查询整个数据库大小
select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data from TABLES;
- 查询指定数据库大小
select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data from TABLES where table_schema='your_database_name';
- 查询指定数据库下某个表的大小
select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data from TABLES where table_schema='your_database_name' and table_name='your_table_name';
- 修改 jdbc.url 配置
jdbc.url=jdbc:mysql://ip-address:port/your_database_name?useUnicode=true&characterEncoding=utf8
- 新增用户
create user ['username']@['localhost'] identified by ['password'];
- 附指定权限
grant all privileges on [database].[table] to ['username']@['localhost'];
- 删除用户
drop user ['username']@['localhost'];
-- 登录时开启导入本地文件功能。不开启这个功能,导入文件时会报“当前 mysql 版本不支持导入文件功能”的错误
mysql -u ${username} --local-infile=1 -p
-- 建表
create table ${tablename}(id int auto_increment not null, username varchar(20) not null, age int not null, primary key(id));
-- 导入 cvs 文件数据。指定编码为 utf-8 ,按照','隔开字段,'\n'换行符隔开一行,指定对应导入的字段,id 字段自增长
load data local infile '${filepath}' into table ${tablename} character set utf8 fields terminated by ',' lines terminated by '\n' (username, age) set id = NULL;
-- 导出表数据到指定文件
select * info outfile '${filepath}' fields terminated by ',' optionally enclosed by '"' lines terminated by '\n' from ${tablename};
# 停止 Mysql 服务 sudo service mysql stop # 以 mysql 的安全模式启动服务 sudo mysqld_safe --skip-grant-tables& # 直接登录 mysql mysql -uroot mysql # 修改 root 密码 mysql > UPDATE user SET password=PASSWORD("password") WHERE user="root"; mysql > FLUSH PRIVILIGES; # 重启 mysql 服务 sudo service mysql restart
Java 连接 Mysql 数据库,字段日期为 0 时,会抛出异常 java.sql.SQLException: Cannot convert value ‘0000-00-00 00:00:00’ from column n to TIMESTAMP
# 解决方法为在配置 JDBC 链接时,添加 zeroDateTimeBehavior 属性来处理 # 1.zeroDateTimeBehavior=exception 默认值,抛出异常 # 2.zeroDateTimeBehavior=convertToNull 将值转为 NULL # 3.zeroDateTimeBehavior=round 将值转为最近的正确值,即'0001-01-01' jdbc:mysql://${mysql.serverUrl}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
- 查看 max_allowed_packet 值
show VARIABLES like '%max_allowed_packet%';
- 修改 max_allowed_packet 值
SET GLOBAL max_allowed_packet=20 * 1024 * 1024
- 修改 /etc/mysql/my.cnf 文件中 [mysqld] 下的配置
max_allowed_packet=20M
int value;
// 将指定位设置为1
value = value | (1 << bit_number);
// 将指定位设置为0
value = value & ~ (1 << bit_number);
// 判断指定位是否为1,为1时表达式结果为非零,0时表达式结果为0
int flag = value & (1 << bit_number);
if (flag) {
printf("第%d位值为1", bit_number);
} else {
printf("第%d位值为0", bit_number);
}
- 查看 jar 包中的内容 $ jar -tf *.jar - 解压出 jar 包中的内容 $ jar -xf *.jar
// byte 数组转 int
public static int bytes2Int(byte[] bytes) {
if (null == bytes) {
return 0;
}
if (bytes.length > 4) {
throw new IllegalArgumentException("byte array length must be less than 4");
}
int value = 0;
for (int i = 0; i < bytes.length; i++) {
int shift = (bytes.length - 1 - i) * 8;
value += (bytes[i] & 0xFF) << shift;
}
return value;
}
// int 转 byte 数组
public static byte[] int2Bytes(int i) {
byte[] result = new byte[4];
result[0] = (byte) ((i >> 24) & 0xFF);
result[1] = (byte) ((i >> 16) & 0xFF);
result[2] = (byte) ((i >> 8) & 0xFF);
result[3] = (byte) (i & 0xFF);
return result;
}
// byte 数组转 char 数组
public static char[] bytes2Chars(byte[] bytes) {
if (null == bytes) {
return null;
}
Charset cs = Charset.forName("UTF-8");
ByteBuffer bb = ByteBuffer.allocate(bytes.length);
bb.put(bytes);
bb.flip();
CharBuffer cb = cs.decode(bb);
return cb.array();
}
// char 数组转 byte 数组
public static byte[] chars2Bytes(char[] chars) {
if (null == chars) {
return null;
}
Charset cs = Charset.forName("UTF-8");
CharBuffer cb = CharBuffer.allocate(chars.length);
cb.put(chars);
cb.flip();
ByteBuffer bb = cs.encode(cb);
return bb.array();
}
Map<Integer, String> data = new LinkedHashMap<>();
for (int i = 0; i < 5; i++) {
data.put(i, "A" + i);
}
ListIterator<Map.Entry<Integer, String>> it = new ArrayList<>(data.entrySet()).listIterator(data.size());
while (it.hasPrevious()) {
Map.Entry<Integer, String> entry = it.previous();
System.out.println("key : " + entry.getKey() + " value : " + entry.getValue());
}
/**
a = a ^ b;
b = b ^ a;
a = a ^ b;
*/
private void swap(int[] nums, int a, int b) {
nums[a] = nums[a] ^ nums[b];
nums[b] = nums[b] ^ nums[a];
nums[a] = nums[a] ^ nums[b];
}
// 每页记录数
int pageSize;
// 总记录数
int rowCount;
// 页数
int pageCount = (rowCount - 1) / pageSize + 1;
/**
* 当数据量较少时,该方法较全部遍历的效率要低
* 当数据量较大且分割大小远小于总数据量时,该方法效率较高
*/
import com.google.common.base.Predicates;
import com.google.common.collect.Maps;
// map 为待分割 map 集合
// 分割大小
int size = 10000;
// map 总大小
int all = map.size();
// 分割结果
List<Map<String, Integer>> res = new ArrayList<>();
// key 的 list 集合
List<String> list = new ArrayList<>(map.keySet());
// 遍历次数
for (int j = 0; j < all / size; j++) {
// Maps,Predicates 为 Google 的 guava 库中的类
Map<String, Integer> subMap = Maps.filterKeys(map, Predicates.in(list.subList(j * size, (j + 1) * size)));
res.add(subMap);
}
// 获取系统的临时文件夹路径
String tmp = System.getProperty("java.io.tmpdir");
- 由于 Java 的泛型在运行时会被擦除,不能够直接获取泛型的类型,但是其实在 class 字节码中还是保存着泛型的信息,可以通过特殊的方式获取到泛型的类型
- 获取父类中的泛型类型
/**
* 定义一个抽象的父类
* 获取父类中的泛型类型 T
*/
public abstract class SuperClass<T> {
// 泛型类型
private Class<T> clazz;
public SuperClass() {
super();
// 根据实现类反射获取包含泛型的父类,然后获取泛型的类型
this.clazz = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getClazz() {
return this.clazz;
}
public static void main(String[] args) {
// 构造匿名子类
SuperClass<String> superClassString = new SuperClass<String>(){};
System.out.println(superClassString.getClazz()); // class java.lang.String
// 构造匿名子类
SuperClass<Entity> superClassEntity = new SuperClass<Entity>(){};
System.out.println(superClassEntity.getClazz()); // class Entity
}
}
- 获取父接口中的泛型类型
public interface SuperInterface<T> {
@SuppressWarnings("unchecked")
default Class<T> getEntityClass() {
Type[] types = getClass().getGenericInterfaces();
for (Type type : types) {
if (type.getTypeName().startsWith(SuperInterface.class.getName())) {
ParameterizedType pt = (ParameterizedType) type;
return (Class<T>)pt.getActualTypeArguments()[0];
}
}
return null;
}
}
public class Children implements SuperInterface<Entity> {
public static void main() {
Children children = new Children();
System.out.println(children.getClass()); // class Entity
}
}
- Entity 对象
/**
* 简单 Java 对象 POJO
*/
class Entity {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- IFoo 接口中有一默认方法 bar
// Interface IFoo
public interface IFoo {
default void bar(int i) {
System.out.println("IFoo.bar(int)");
}
}
- Foo 实现 IFoo 接口,并也有一个公共方法 bar
// Class Foo
public class Foo implements IFoo {
public void bar(long l) {
System.out.println("Foo.bar(long)");
}
public static void main(String[] args) {
Foo foo = new Foo();
foo.bar(42); // 1 IFoo.bar(int)
IFoo ifoo = foo;
ifoo.bar(42); // 2 IFoo.bar(int)
}
}
- main 方法中 1,2 位置调用的方法为都是接口中默认的方法,因为接口中的默认方法提供了更加准确的匹配
// 当对象的属性对象为 null 时,复制对象中的数值类型属性被初始化为 0
BeanUtils.copyProperties(entity, copyEntity);
/* 方式1 */
// 设置所有类型的默认值为 null
BeanUtilsBean.getInstance().getConvertUtils().register(false, true, 0);
/* 方式2 */
// 根据需要,注册对应的 Converter 对象, 并设置对应的默认值
ConvertUtils.register(new LongConverter(null), Long.class); // Long
ConvertUtils.register(new DoubleConverter(null), Double.class); // Double
ConvertUtils.register(new IntegerConverter(null), Integer.class); // Integer
ConvertUtils.register(new FloatConverter(null), Float.class); // Float
...
// 再复制属性
BeanUtils.copyProperties(entity, copyEntity);
- 加密
/**
* @param content 待加密内容
* @param password 密钥
* @return 密文,加密异常时返回 null
*/
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(byteContent);
} catch (NoSuchAlgorithmException | NoSuchPaddingException |
InvalidKeyException | UnsupportedEncodingException |
IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
- 解密
/**
* @param content 待解密内容
* @param password 密钥
* @return 原内容,解密异常时返回 null
*/
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(content);
} catch (NoSuchAlgorithmException | NoSuchPaddingException |
InvalidKeyException | IllegalBlockSizeException |
BadPaddingException e) {
e.printStackTrace();
}
return null;
}
- 使用
public static void main(String[] args) {
String content = "test"; // 待加密内容
String pwd = "AES"; // 密钥
String encryptContent = Base64.getEncoder().encodeToString(encrypt(content, pwd)); // 密文
String decryptContent = new String(decrypt(Base64.getDecoder().decode(encryptContent), pwd)); // 解密密文
System.out.println(content.equals(decryptContent));
}
/**
* 匹配国际电话号码
* 13987654321
* +8613987654321
* +86139-876-54321
*/
public static final Pattern PHONE_PATTERN = Pattern.compile("([+]?\\d{1,2}[.\\-\\s]?)?(\\d{3}[.-]?){2}\\d{2,5}");
/**
* 匹配电子邮箱
* saisimon@gmail.com
*/
public static final Pattern EMAIL_PATTERN = Pattern.compile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?");
/**
* 匹配 18 位身份证号
* 43011319990216213X
*/
public static final Pattern ID_CARD_PATTERN = Pattern.compile("^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9]|X)$");
* . ? + $ ^ [ ] ( ) { } | \ /
- 使用 Java 1.7 中 Files 提供的方法
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
private static final String defaultType = "application/octet-stream";
public static String parseContentType(File file) {
String contentType = defaultType;
if (file != null) {
Path path = Paths.get(file.getAbsolutePath());
try {
contentType = Files.probeContentType(path);
} catch (IOException e) {
LOG.error("Unknown Content-Type", e);
}
}
return contentType;
}
- 添加 JSch 依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>${jsch.version}</version>
</dependency>
- SSH 属性类
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(exclude="password")
@ConfigurationProperties(prefix="ssh")
public class SshProperties {
private String host;
private Integer port;
private String username;
private String password;
private Forward forward;
@Getter
@Setter
@ToString
public static class Forward {
private String fromHost;
private Integer fromPort;
private String toHost;
private Integer toPort;
}
}
- SSH 配置类
import javax.annotation.PreDestroy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import cn.comein.ssh.config.SshProperties.Forward;
import lombok.extern.slf4j.Slf4j;
@Configuration
@EnableConfigurationProperties(SshProperties.class)
@ConditionalOnProperty(prefix = "ssh", value = "enabled", havingValue = "true", matchIfMissing = false)
@Slf4j
// 实现 LoadTimeWeaverAware 接口是因为需要 SSH 正向代理需要在EntityManagerFactory加载前运行
public class SshConfiguration implements LoadTimeWeaverAware {
private final Session session;
public SshConfiguration(SshProperties sshProperties) {
Session session = null;
try {
// 可以自行为 JSch 添加日志,需要实现 com.jcraft.jsch.Logger 接口
// JSch.setLogger(new JSchLogger())
session = new JSch().getSession(sshProperties.getUsername(), sshProperties.getHost(), sshProperties.getPort());
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(sshProperties.getPassword());
session.connect();
Forward forward = sshProperties.getForward();
if (forward != null) {
session.setPortForwardingL(forward.getFromHost(), forward.getFromPort(), forward.getToHost(), forward.getToPort());
log.info("{}:{} -> {}:{}", forward.getFromHost(), forward.getFromPort(), forward.getToHost(), forward.getToPort());
}
} catch (JSchException e) {
log.error("ssh " + sshProperties.getHost() + " failed.", e);
}
this.session = session;
}
@PreDestroy
// 配置销毁时,断开 SSH 链接
public void disconnect() {
if (session != null) {
session.disconnect();
}
}
@Override
public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
}
}
- application.properties 文件中添加 SSH 相关属性值
ssh.enabled=false # 是否启用 SSH 配置 ssh.host=127.0.0.1 # SSH 地址 ssh.port=22 # SSH 端口 ssh.username= # SSH 用户名 ssh.password= # SSH 密码 ssh.forward.from_host= # 绑定的本地地址 ssh.forward.from_port= # 绑定的本地端口 ssh.forward.to_host= # 正向代理的远程地址 ssh.forward.to_port= # 正向代理的远程端口
- 修改数据源为绑定的本地地址与端口
- SSH 连接失败可能的原因
- 用户名或密码错误
- sshd_config 中需要配置 PasswordAuthentication yes,允许使用密码登陆
- 使用 root 用户登陆,需要配置 PermitRootLogin yes,才能登陆
package net.saisimon.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import lombok.extern.slf4j.Slf4j;
/**
* <p>Jackson JSON 工具类</p>
*
* @author Saisimon
*/
@Slf4j
public class JsonUtils {
/**
* <p>对象转 JSON</p>
*
* @param obj 目标对象
* @return JSON 串
*/
public static String toJson(Object obj) {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.error("生成 json 失败", e);
return null;
}
}
/**
* <p>解析 JSON 串,支持泛型</p>
* <p>目标类为 Map<String, List<Long>></p>
* <p>targetClass => Map.class</p>
* <p>genericClasses => String.class, List.class, Long.class</p>
*
* @param json JSON 串
* @param targetClass 目标类
* @param genericClasses 目标类的泛型数组
* @return 目标对象
*/
public static <T> T fromJson(String json, Class<T> targetClass, Class<?>... genericClasses) {
try {
ObjectMapper objectMapper = new ObjectMapper();
JavaType javaType = parse(targetClass, genericClasses);
return objectMapper.readValue(json, javaType);
} catch (IOException e) {
log.error("解析 json 失败", e);
return null;
}
}
private static JavaType parse(Class<?> targetClass, Class<?>... genericClasses) {
if (genericClasses == null) {
return TypeFactory.defaultInstance().constructType(targetClass);
} else {
List<Class<?>> genericClassList = new ArrayList<>(Arrays.asList(genericClasses));
return construct(targetClass, parse(genericClassList, genericClassList.iterator()));
}
}
private static List<JavaType> parse(List<Class<?>> genericClassList, Iterator<Class<?>> it) {
List<JavaType> javaTypes = new ArrayList<>();
while (it.hasNext()) {
Class<?> cls = it.next();
int genericLenght = cls.getTypeParameters().length;
if (genericLenght > 0) {
List<JavaType> subJavaTypes = parse(genericClassList.subList(1, genericClassList.size()), it);
JavaType javaType = construct(cls, subJavaTypes.subList(0, genericLenght));
javaTypes.add(javaType);
javaTypes.addAll(subJavaTypes.subList(genericLenght, subJavaTypes.size()));
} else {
JavaType javaType = TypeFactory.defaultInstance().constructType(cls);
javaTypes.add(javaType);
}
}
return javaTypes;
}
private static JavaType construct(Class<?> cls, List<JavaType> javaTypes) {
return TypeFactory.defaultInstance().constructParametricType(cls, javaTypes.toArray(new JavaType[javaTypes.size()]));
}
}
JUnit 中提供了三种方式来决定执行顺序
- MethodSorters.NAME_ASCENDING 按照测试方法的方法名的字母表顺序进行排序
- MethodSorters.JVM 交由 JVM 决定执行顺序
- MethodSorters.DEFAULT 按照测试方法的方法名的 hashcode 进行排序,这个为默认值
具体实现可在 org.junit.internal.MethodSorter 中找到,可以通过在测试用例类上添加 FixMethodOrder 注解来改变默认值。但这种方式不能按照指定的顺序执行测试用例,下面提供一个方法来实现这个功能。
- 定义 Order 注解,来指定测试用例的执行顺序
package net.saisimon.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
int value() default 0;
}
- 继承 JUnit 的 org.junit.runners.BlockJUnit4ClassRunner 运行器,来重新实现获取测试用例顺序的方法,如果是测试 Spring 应用,可继承 org.springframework.test.context.junit4.SpringJUnit4ClassRunner 运行器
package net.saisimon.test;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
// Spring
// import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
// JUnit
import org.junit.runners.BlockJUnit4ClassRunner;
// Spring 继承
// public class OrderedRunner extends SpringJUnit4ClassRunner {
// JUnit 继承
public class OrderedRunner extends BlockJUnit4ClassRunner {
// 测试用例的方法集合
private static List<FrameworkMethod> testMethodList;
public OrderedRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
// 重写 computeTestMethods 方法,按指定顺序排序
@Override
protected List<FrameworkMethod> computeTestMethods() {
if (testMethodList == null) {
testMethodList = super.computeTestMethods().stream()
.sorted((m1, m2) -> {
// 根据测试用例上的 Order 注解来决定执行顺序
Order o1 = m1.getAnnotation(Order.class);
Order o2 = m2.getAnnotation(Order.class);
if (o1 == null || o2 == null) {
return 0;
}
return o1.value() - o2.value();
}).collect(Collectors.toList());
}
return testMethodList;
}
}
- 测试用例,使用 Order 注解来决定执行顺序
package net.saisimon.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import net.saisimon.annotation.Order;
@RunWith(OrderedRunner.class)
public class OrderedRunnerTest {
@Test
@Order(1)
public void test2() {
System.out.println(2);
}
@Test
@Order(2)
public void test1() {
System.out.println(1);
}
@Test
@Order(3)
public void test3() {
System.out.println(3);
}
// 输出
// 2
// 1
// 3
}
- 修改 web.xml 文件, 添加 CharacterEncodingFilter
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- filter 需要放在所有 filter 的前面才会生效
文件扩展名 | ContentType |
---|---|
.html | text/html |
.doc | application/msword |
.ppt | application/vnd.ms-powerpoint |
.xls | application/vnd.ms-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.xml | text/xml |
.txt | text/plain |
application/pdf | |
.jpeg | image/jpeg |
.js | application/x-javascript |
.css | text/css |
.*(未知二进制流) | application/octet-stream |
/*
<body>
<div>
<ul id="meun">
<li class="sub_meun" name="food"></li>
<li class="sub_meun" name="phone">
<p>// Phone</p>
<span>
<a> go </a>
<span>
</li>
<li class="sub_meun" name="ring"></li>
</ul>
</div>
</body>
选取内容为 go 的 a 标签
*/
String xpath = "//ul[@id='meun']/li[@class='sub_meun' and @name='phone']/p/parent::li/span/a[normalize-space(text())='go']";
# 生成 CA 私钥 $ openssl genrsa -out ca.key 2048 # 生成 CA 证书 $ openssl req -x509 -new -key ca.key -out ca.crt # 生成服务端私钥 $ openssl genrsa -out server.key 2048 # 生成服务端证书请求文件 $ openssl req -new -key server.key -out server.csr # 使用CA证书生成服务端证书 $ openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out server.crt # 服务端证书转为 pkcs12 格式 $ openssl pkcs12 -export -in server.crt -inkey server.key -out server.pkcs12 # 生成服务端的keystore $ keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server.jks -srcstoretype pkcs12
- 根据 User-Agent 获取编码后的 Content-Disposition 值
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.mail.internet.MimeUtility;
public static String encodeDownloadContentDisposition(String userAgent, String filename) {
try {
String newFilename = URLEncoder.encode(filename, "UTF8");
// 如果没有UA,则默认使用IE的方式进行编码
String cd = "attachment; filename=\"" + newFilename + "\"";
if (userAgent != null) {
userAgent = userAgent.toLowerCase();
if (userAgent.indexOf("msie") != -1) { // IE浏览器,只能采用URLEncoder编码
cd = "attachment; filename=\"" + newFilename + "\"";
} else if (userAgent.indexOf("opera") != -1) { // Opera浏览器只能采用filename*
cd = "attachment; filename*=UTF-8''" + newFilename;
} else if (userAgent.indexOf("safari") != -1) { // Safari浏览器,只能采用ISO编码的中文输出
cd = "attachment; filename=\"" + new String(filename.getBytes("UTF-8"), "ISO8859-1") + "\"";
} else if (userAgent.indexOf("applewebkit") != -1) { // Chrome浏览器,只能采用MimeUtility编码或ISO编码的中文输出
newFilename = MimeUtility.encodeText(filename, "UTF8", "B");
cd = "attachment; filename=\"" + newFilename + "\"";
} else if (userAgent.indexOf("mozilla") != -1) { // FireFox浏览器,可以使用MimeUtility或filename*或ISO编码的中文输出
cd = "attachment; filename*=UTF-8''" + newFilename;
}
}
return cd;
} catch (UnsupportedEncodingException e) {
return "attachment; filename=\"" + filename + "\"";
}
}
- 创建Maven的普通java项目 $ mvn archetype:create -DgroupId=[packageName] -DartifactId=[projectName] - 创建Maven的Web项目 $ mvn archetype:create -DgroupId=[packageName] -DartifactId=[webappName] -DarchetypeArtfactId=maven-archetype-webapp - 编译源码 $ mvn compile - 打包 $ mvn package - 在本地Repository中安装jar $ mvn install - 清理项目 $ mvn clean - 生成eclipse/idea项目 $ mvn eclipse:eclipse $ mvn idea:idea - 生成站点信息 $ mvn site
- 跳过单元测试
# 直接跳过测试,测试类不会被编译 $ mvn install -Dmaven.test.skip=true # 跳过测试运行,但会编译测试类 $ mvn install -DskipTests
- 添加编译插件
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.X</source> <target>1.X</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
- pom 文件添加 tools 依赖
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.x</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>net.saisimon</groupId>
<artifactId>local-jar</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>/local/path/local-jar-1.0.jar</systemPath>
</dependency>
- $MAVEN_HOME/conf/settings.xml
<settings>
...
<servers>
...
<server>
<id>nexus-snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
...
</servers>
...
</settings>
$ mvn deploy:deploy-file -DgroupId=net.saisimon # groupId -DartifactId=test # artifactId -Dversion=0.0.1-SNAPSHOT # version -Dpackaging=jar # packaging -Dfile=/file/test/target/test-0.0.1-SNAPSHOT.jar # file path -Durl=http://127.0.0.1:8081/repository/maven-3rd/ # deploy url -DrepositoryId=nexus-snapshots # settings.xml 中 Servers 配置的 ID 名称
#!/bin/bash
profileName=$1
if [ ! ${profileName} ]; then
profileName='dev'
fi
# -DskipTests 跳过测试
# -P 激活指定的 profile
# -U 强制更新releases、snapshots类型的插件或依赖库
# -pl 选择需要构建的项目,选项后可跟随{groupId}:{artifactId}或者所选模块的相对路径
mvn clean install -DskipTests -P ${profileName} -U && mvn spring-boot:run -DskipTests -pl {groupId}:{artifactId}/{relativePath}
- 其主要原因为当前 Tomcat 版本与该 Web 项目的Web版本不兼容,Tomcat 6支持 Web 2.5及以下版本,tomcat 7支持 Web 3.0及以下版本
- 在 Eclipse 中:Project -> Properties -> Project Facets -> Dynamic Web Module,检查 Web 项目的Web版本
- Eclipse 环境下的修改方法为:项目根目录找到 .setting 文件夹中的 org.eclipse.wst.common.project.facet.core.xml 文件,修改其中 jst.web 的 version 的值至当前 Tomcat 支持的版本
- 更新 Tomcat 版本,使其与 Web 版本兼容
<!-- docBase为webapp的路径 path为发布的路径,根目录访问这里留空 -->
<!-- Context 标签配置在 Tomcat 目录下 conf 文件里的 Server.xml 配置文件中 -->
<Server>
<Service>
<Engine>
<Host>
<Context docBase="[webapp_path]" path="" reloadable="true"/>
</Host>
</Engine>
</Service>
</Server>
- 修改 Web 项目的 Context Path
- 打开 web project folder >> .setting >> org.eclipse.wst.common.component 文件
- 编辑该文件,修改其中 content-root 属性为空值
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="webapp">
...
<property name="context-root" value=""/>
</wb-module>
</project-modules>
- HelloWorld.class 在 net.saisimon.jni 包中 $ javah HelloWorld Error: Could not find class file for 'HelloWorld'. - HelloWorld 在 Java 包中,需要到包的根目录执行 javah 命令 $ cd ../../../ $ javah net.saisimon.jni.HelloWorld - 即可生成头文件 net_saisimon_jni_HelloWorld.h
package net.saisimon.test
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.codec.digest.DigestUtils;
public class Test implements Runnable {
volatile int vote = 0;
CountDownLatch cdl = new CountDownLatch(5);
@Override
public void run() {
parse();
// 递减计数器
cdl.countDown();
}
public void parse() {
while (vote < 10) {
int x = 0;
int v = vote;
vote++;
String tmp = "Saisimon" + v + x;
String md5 = DigestUtils.md5Hex(tmp);
while (!md5.startsWith("000000")) {
x++;
tmp = "Saisimon" + v + x;
md5 = DigestUtils.md5Hex(tmp);
}
System.out.println("thread : " + Thread.currentThread().getName() + " , vote : " + v + " , x : " + x);
}
}
public static void main(String[] args) {
Test t = new Test();
long start = System.currentTimeMillis();
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
es.execute(t);
}
es.shutdown();
try {
// 计数器减至零时,await 会被执行
p.cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("多线程耗时:" + (System.currentTimeMillis() - start));
}
}
http://localhost:8983/solr/item/update?commit=true&stream.file=d:/tmp/solr_data.csv&stream.contentType=application/csv
- 参数中有没有序列化的对象。所有参数必须继承 Serializable 接口实现序列化
- 参数对象中有不能序列化的属性。改变属性,使所有属性可以序列化
- 服务提供者与服务消费者依赖版本不一致,导致序列化异常。保证提供者和消费者依赖版本一致
<!-- Java 要求 -->
<!-- Hadoop 2.7 以及之后的版本需要 JDK 7 -->
<!-- Hadoop 2.6 以及之前的版本支持 JDK 6 -->
<!-- Hadoop 1.x.y 依赖 hadoop-core 包 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>1.x.y</version>
</dependency>
<!-- Hadoop 2.x.y 依赖 hadoop-common、hadoop-hdfs、hadoop-mapreduce-client-core、hadoop-client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.x.y</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.x.y</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.x.y</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.x.y</version>
</dependency>
- 修改配置文件 {workspaceHome}/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs
SHOW_MEMORY_MONITOR=true
Project - Properties - Java Code Style - Formatter - Enable project specific settings - 新建一个配置 - Line Wrapping - 设置 Maximum line width
- 在 Eclipse Marketplace 安装 ANSI Escape in Console 插件
- 下载 java-color-loggers.jar 包,并加入到 build path 中
- 在 log4j.properties 文件中添加如下代码
log4j.appender.CONSOLE=com.colorlog.log4j.AnsiColorConsoleAppender # You can change the default colors # log4j.appender.CONSOLE.FatalColour={esc}[1;35m # log4j.appender.CONSOLE.ErrorColour={esc}[0;31m # log4j.appender.CONSOLE.WarnColour ={esc}[0;33m # log4j.appender.CONSOLE.InfoColour ={esc}[1;32m # log4j.appender.CONSOLE.DebugColour={esc}[1;36m # log4j.appender.CONSOLE.TraceColour={esc}[1;30m
- Project - Predicates - Project Facets - 选择 Java 和 Dynamic Web Module (选择对应版本)
- 当选择了 Dynamic Web Module 后,下方选择 Further Configuration availabe (没有这个选项的话,删除项目中 eclipse 生成的 .settings 文件夹,在 eclipse 中 refresh 项目,然后重复上一步)
- 设置 classes 路径 和 webroot 的路径 - 保存
- Project - Predicates - Deployment Assembly - Add 添加 Libraries
Project Explorer 标签栏 -> v(View Menu) -> Customize View… -> Filters -> 勾选/取消勾选 Closed projects
/**
* 添加自定义的监听器,拦截器,过滤器
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(WebConfig.class);
/**
* 添加监听器
*/
@Bean
public ServletListenerRegistrationBean<EventListener> doMyListener() {
if (LOG.isDebugEnabled()) {
LOG.debug("Do My Listener");
}
ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(new MyListener());
return registrationBean;
}
/**
* 添加过滤器
*/
@Bean
public FilterRegistrationBean doMyFilter() {
if (LOG.isDebugEnabled()) {
LOG.debug("Do My Filter");
}
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*"); //拦截路径,可以添加多个
registration.setName("myFilter");
registration.setOrder(1);
return registration;
}
/**
* 添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (LOG.isDebugEnabled()) {
LOG.debug("Do My Interceptors");
}
registry.addInterceptor(new MyInterceptor());
super.addInterceptors(registry);
}
}
- 该方法在 Spring Boot 以 java -jar 启动时获取的路径不正确,改用文件流的形式
import org.springframework.util.ClassUtils;
// need handler URISyntaxException
String classpath = ClassUtils.getDefaultClassLoader().getResource("").toURI().getPath();
- ClassUtils#getDefaultClassLoader()
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoade
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
- 简单在 application.properties 中配置国际化信息
# INTERNATIONALIZATION (MessageSourceAutoConfiguration) spring.messages.always-use-message-format=false # Set whether to always apply the MessageFormat rules, parsing even messages without arguments. spring.messages.basename=package # 默认为 messages spring.messages.cache-seconds=60 # 默认为 -1 spring.messages.encoding=UTF-8 spring.messages.fallback-to-system-locale=true # Set whether to fall back to the system Locale if no files for a specific Locale have been found.
- 自定义配置国际化信息
@Configuration
public class I18NConfig extends WebMvcConfigurerAdapter {
@Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// 设置 Spring 读取语言包的位置 src/main/resources/package_*.properties 文件
messageSource.setBasename("classpath:package");
// 设置默认编码为 UTF-8
messageSource.setDefaultEncoding("UTF-8");
// 设置当 code 没找到对应的文本时默认使用 code 作为其文本
messageSource.setUseCodeAsDefaultMessage(true);
// 设置缓存时长
messageSource.setCacheSeconds(60);
return messageSource;
}
@Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
// 设置默认地区,对应文件名为 package_cn.properties
localeResolver.setDefaultLocale(new Locale("cn"));
return localeResolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 设置改变地区的参数名
// http://www.xxx.com/index?language=cn 语言为中文
// http://www.xxx.com/index?language=en 语言为English
lci.setParamName("language");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(localeChangeInterceptor());
}
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 设置默认首页跳转至 /login
registry.addViewController("/").setViewName("redirect:/login");
}
}
- 将 SpringContext 放在 Spring 的扫描路径内
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy(false)
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (null == SpringContext.applicationContext) {
SpringContext.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
- 如果 SpringContext 无法被 Spring 扫描到,则需要在启动类里直接引入,并且 SpringContext 中不需要添加@Component注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(SpringContext.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- config.properties 放在 classpath 路径下
# 超时时间 config.timeout=10000
- Properties.java 使用 PropertySource 注解设置文件路径,使用 Value 注解注入属性值
package net.saisimon.utils
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:email/config.properties") // 设置 Properties 文件路径
public class Properties {
// 设置默认值为 10000
@Value("${config.timeout:10000}")
private Long timeout;
public Long getTimeout() {
return timeout;
}
}
- https.properties 放在 classpath 路径下
https.port=443 https.secure=true https.scheme=https https.ssl=true https.keystore=/keystore/dir/server.jks https.keystore-password=********
- WebConfig.java Web 配置类中添加处理 HTTPS 请求的 Container
package net.saisimon.config; import java.io.File; import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import lombok.Data; @Configuration @PropertySource("classpath:https.properties") @EnableConfigurationProperties(WebConfig.SslProperties.class) public class WebConfig { @Bean public EmbeddedServletContainerFactory servletContainer(SslProperties properties) { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() { @Override protected void postProcessContext(Context context) { SecurityConstraint securityConstraint = new SecurityConstraint(); securityConstraint.setUserConstraint("CONFIDENTIAL"); SecurityCollection collection = new SecurityCollection(); collection.addPattern("/*"); securityConstraint.addCollection(collection); context.addConstraint(securityConstraint); } }; tomcat.addAdditionalTomcatConnectors(createSslConnector(properties)); return tomcat; } private Connector createSslConnector(SslProperties properties) { Connector connector = new Connector(); properties.configureConnector(connector); return connector; } @ConfigurationProperties(prefix = "https") @Data public static class SslProperties { private Integer port; private Boolean ssl = true; private Boolean secure = true; private String scheme = "https"; private File keystore; private String keystorePassword; public void configureConnector(Connector connector) { if (port != null) { connector.setPort(port); } if (secure != null) { connector.setSecure(secure); } if (scheme != null) { connector.setScheme(scheme); } if (ssl != null) { connector.setProperty("SSLEnabled", ssl.toString()); } if (keystore != null && keystore.exists()) { connector.setProperty("keystoreFile", keystore.getAbsolutePath()); connector.setProperty("keystorePass", keystorePassword); } } } }
- https.properties 放在 classpath 路径下,内容同上
- WebConfig.java Web 配置类中添加处理 HTTPS 请求的 Connector
package net.saisimon.config; import java.io.File; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import lombok.Data; @Configuration @PropertySource("classpath:https.properties") @EnableConfigurationProperties(WebConfig.SslProperties.class) public class WebConfig { @Bean public EmbeddedServletContainerFactory servletContainer(SslProperties properties) { JettyEmbeddedServletContainerFactory jetty = new JettyEmbeddedServletContainerFactory() { @Override protected void postProcessWebAppContext(WebAppContext webAppContext) { // 所有请求都必须为 Https 协议 ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); ConstraintMapping mapping = new ConstraintMapping(); Constraint constraint = new Constraint(); constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL); mapping.setConstraint(constraint); mapping.setPathSpec("/*"); securityHandler.addConstraintMapping(mapping); webAppContext.setSecurityHandler(securityHandler); } }; jetty.addServerCustomizers(new JettyServerCustomizer() { @Override public void customize(Server server) { // 移除Spring Boot 生成的 Connector int httpPort = 80; Connector[] connectors = server.getConnectors(); for (Connector connector : connectors) { if (connector instanceof ServerConnector) { httpPort = ((ServerConnector) connector).getPort(); } server.removeConnector(connector); } // 配置 Http 协议的 Connector HttpConfiguration httpConfig = new HttpConfiguration(); // 重定向 httpConfig.setSecureScheme(properties.getScheme()); httpConfig.setSecurePort(properties.getPort()); httpConfig.addCustomizer(new SecureRequestCustomizer()); ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); httpConnector.setPort(httpPort); server.addConnector(httpConnector); // 配置 Https 协议的 Connector HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); httpsConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory connectionFactory = new HttpConnectionFactory(httpsConfig); SslContextFactory sslContextFactory = new SslContextFactory(); if (null != properties.getKeystore() && properties.getKeystore().exists()) { sslContextFactory.setKeyStorePath(properties.getKeystore().getAbsolutePath()); sslContextFactory.setKeyStorePassword(properties.getKeystorePassword()); } SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); ServerConnector serverConnector = new ServerConnector(server, sslConnectionFactory, connectionFactory); serverConnector.setPort(properties.getPort()); server.addConnector(serverConnector); } }); return jetty; } @ConfigurationProperties(prefix = "https") @Data public static class SslProperties { private Integer port; private Boolean ssl = true; private Boolean secure = true; private String scheme = "https"; private File keystore; private String keystorePassword; } }
<<Spring Boot 中获取 classpath 下的文件资源>>
import org.springframework.core.io.ClassPathResource;
ClassPathResource classPathResource = new ClassPathResource("path/file.xml");
InputStream in = classPathResource.getInputStream();
/**
* 装配非 Spring 管理对象
*
* 获取 ApplicationContext 请参考[Spring Boot 获取 ApplicationContext]
*
* @param obj non-spring object
* @return spring object
* @see SpringContext#getApplicationContext
*/
public static Object autowire(Object obj) {
if (obj == null) {
return null;
}
getApplicationContext().getAutowireCapableBeanFactory().autowireBean(obj);
return obj;
}
- POM 文件中添加 WebSocket 依赖
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
...
- 配置注册 WebSocket 的处理器
package net.saisimon.config
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import net.saisimon.handler.MyHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册处理器,映射到 /websocket
// 并添加 HttpSessionHandshakeInterceptor 拦截器,获取 HttpSession
registry.addHandler(myHandler(), "/websocket")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
@Bean
public WebSocketHandler myHandler() {
// 处理类
return new MyHandler();
}
}
- 实现处理类
package net.saisimon.handler;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
// 继承抽象处理类,实现对应的处理方法
public class MyHandler extends AbstractWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 处理文本文件
String msg = new String(message.asBytes(), "UTF-8");
System.out.println(msg);
// 发送消息
session.sendMessage(new TextMessage(msg));
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
// 处理二进制文件
}
@Override
protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
// 处理 Pone 消息
}
}
- 直接在 Mysql 指定默认引擎和编码
- 代码指定生成的引擎和编码
- 继承 MySQL5Dialect,重写 getTableTypeString 方法,返回引擎和编码
package net.saisimon.config;
import org.hibernate.dialect.MySQL5Dialect;
public class MySQL5UTF8Dialect extends MySQL5Dialect {
@Override
public String getTableTypeString() {
// 指定引擎和编码
return " ENGINE=InnoDB DEFAULT CHARSET=utf8";
}
}
- 在 application.properties 中配置 Hibernate 方言
# JPA spring.jpa.properties.hibernate.dialect=net.saisimon.config.MySQL5UTF8Dialect
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Validator;
/**
* 验证对象属性是否符合预期,支持 validation-api 与 hibernate-validator 验证注解
*
* @param target 待验证对象
* @param validator Spring 验证器,可直接注入获取 Validator 对象或自行实现 Validator 接口
* @param groups 分组标识类,对应验证注解中的 groups 属性
* @return 返回验证结果
*/
public static BindingResult validate(Object target, Validator validator, Class<?>... groups) {
DataBinder binder = new DataBinder(target);
binder.setValidator(validator);
List<Class<?>> list = new ArrayList<>();
if (groups != null) {
for (Class<?> group : groups) {
if (group != null) {
list.add(group);
}
}
}
binder.validate(list.toArray());
return binder.getBindingResult();
}
package net.saisimon.util;
import java.io.IOException;
import java.util.List;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
public class PropertyUtils {
public static Object fetchProperties(PropertySourceLoader loader, String name, String key) {
try {
ClassPathResource classPathResource = new ClassPathResource(name);
List<PropertySource<?>> propertySources = loader.load(name, classPathResource);
if (propertySources.size() == 0) {
return null;
}
return propertySources.get(0).getProperty(key);
} catch (IOException e) {
return null;
}
}
/**
* 读取 application.yml 文件的属性
*
*/
public static Object fetchYaml(String key, Object defaultValue) {
Object value = fetchProperties(new YamlPropertySourceLoader(), "application.yml", key);
return value == null ? defaultValue : value;
}
/**
* 读取 application.properties 文件的属性
*
*/
public static Object fetchProperty(String key, Object defaultValue) {
Object value = fetchProperties(new PropertiesPropertySourceLoader(), "application.properties", key);
return value == null ? defaultValue : value;
}
}
- 使用 @EnableTransactionManagement 注解开启事物管理,使用 @Transactional 注解控制事物,未指定 value 值,则使用默认指定事务管理器
package net.saisimon.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
@Configuration
public class TransactionManagementConfig implements TransactionManagementConfigurer {
@Autowired
private PlatformTransactionManager transactionManager;
@Bean(name = "transactionManager")
@Primary
public PlatformTransactionManager transactionManager(@Qualifier("devDB") DataSource devDB) {
return new DataSourceTransactionManager(devDB);
}
@Bean(name = "testDBTransactionManager")
public PlatformTransactionManager testDBTransactionManager(@Qualifier("testDB") DataSource testDB) {
return new DataSourceTransactionManager(testDB);
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
// 设置默认注解事物管理器
return transactionManager;
}
}
- 使用 -2 和 -3 区分 Python 版本
# python2 运行 main.py py -2 main.py # python3 运行 main.py py -3 main.py
- 代码中指明使用的 Python 解释器版本
# 指定 Python2 解释器 写在代码第一行
#! python2
# 指定 Python3 解释器 写在代码第一行
#! python3
# -*- coding:utf-8 -*-
# Python 在 Windows 平台上区分文本文件和二进制文件;读取或写入文本文件中时, 行尾字符会被自动地稍加改变。
# 在读写二进制文件时,要选择二进制模式打开。
file = open(filename, 'wb')
- 确定系统中安装了 Python $ python -V Python 2.6.6 - 方式一: 使用 yum 安装 SCons $ yum install scons - 方式二: 使用安装包安装 SCons - 去官网下载 SCons 安装包,解压 $ tar -xzvf scons-2.4.1.tar.gz - 编译安装,默认安装路径 /usr/lib/scons-2.4.1 $ python setup.py install [--prefix=/xx/xx] - 查看版本号 $ scons -v
.justify {
text-align:justify;
text-justify:distribute-all-lines; /*ie6-8*/
text-align-last:justify; /* ie9*/
-moz-text-align-last:justify; /*ff*/
-webkit-text-align-last:justify; /*chrome 20+*/
}
<div class="text">This is some long text that will not fit in the box</div>
<style>
.text {
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
body::-webkit-scrollbar {
width: 8px;
height: 8px;
background-color: #F5F5F5;
}
body::-webkit-scrollbar-thumb {
background-color: #666;
}
- HTML
<div class="content">
<div class="marquee">
<span>A超出文本测试1 B超出文本测试2 C超出文本测试3 D超出文本测试4</span>
</div>
</div>
- 样式, 鼠标移动到文字上时执行滚动动画
.content {
width: 300px;
border: 1px solid #000;
font-size: 20px;
margin: auto;
height: 50px;
line-height: 50px;
padding: 0px 5px;
}
.marquee {
white-space: nowrap; /* 不换行 */
overflow-x: hidden;
width: 100%;
}
.marquee span:hover {
display: inline-block;
animation: marquee 5s linear infinite; /* 执行 marquee 动画,5秒一次,线性,无限循环 */
}
@keyframes marquee {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-100%, 0);
}
}
- 是否为数字
function isNumber(obj) { return obj === +obj; }
- 是否为字符串
function isString(obj) { return obj === obj + ''; }
- 是否为布尔类型
function isBoolean(obj) { return obj === !!obj; }
- 是否为数组
function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }
function toCapitalizeCase(str) {
if (!str || str.length === 0) {
return str;
}
if (str.length == 1) {
return str.toUpperCase();
} else {
return str[0].toUpperCase() + str.substring(1);
}
}
Date.prototype.format = function(style) {
var o = {
"M+" : this.getMonth() + 1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"w+" : "\u65e5\u4e00\u4e8c\u4e09\u56db\u4e94\u516d".charAt(this.getDay()), //week
"q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
"S" : this.getMilliseconds() //millisecond
}
if (/(y+)/.test(style)) {
style = style.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for(var k in o){
if (new RegExp("("+ k +")").test(style)){
style = style.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return style;
};
/*
* 示例
* 2016-12-01 19:12:28
*/
var formatDate = new Date().format("yyyy-MM-dd hh:mm:ss");
- 假值有 false, null, 0, “”, undefined 和 NaN
function filterFalse(arr) {
return arr.filter(function(value) {
return Boolean(value);
});
}
console.log(filterFalse([false, null, "aaa", 7, 0, "", NaN]));
// -> ["aaa", 7]
- 各浏览器的对 LocalStorage 的支持情况
IE | Firefox | Chrome | Opera | Safari | IOS | Android |
8.0+ | 3.5+ | 4.0+ | 11.5+ | 4.0+ | 3.2+ | 2.1+ |
- LocalStorage 的使用
// 判断浏览器是否支持 LocalStorage
if(window.localStorage){
localStorage.username = "Saisimon"; // 设置 username 为"Saisimon"
localStorage["username"] = "Simon"; // 设置 username 为 "Simon",会覆盖上面的设置
localStorage.setItem("username", "Saisimon"); // 同上
var username = localStorage["username"]; // 获取 username 的值
username = localStorage.username; // 同上
username = localStorage.getItem("username"); // 同上
localStorage.removeItem("username"); // 清除 username 的值
localStorage.clear(); // 清除 LocalStorage 中的所有值
// 打印出 LocalStorage 中的所有值
var storage = window.localStorage;
for (var i = 0; i < storage.length; i++) {
console.log(storage.key(i) + " : " + storage.getItem(storage.key(i));
}
} else {
alert('Your browser does not support LocalStorage.');
}
// index 添加/删除值的位置
// howmany 要删除的数量,添加时设置为0
// item1, ..., itemX 向数组添加的新值
arrayObject.splice(index, howmany, item1, ....., itemX)
var arrayObject = ['1', '2', '3']
// 插入
arrayObject.splice(2, 0, '2') // ['1', '2', '2', '3']
arrayObject.splice(4, 0, '2') // ['1', '2', '2', '3', '2']
arrayObject.splice(-1, 0, '5') // ['1', '2', '2', '3', '5', '2']
// 删除
arrayObject.splice(4, 0) // ['1', '2', '2', '3', '2']
arrayObject.splice(0, 3) // ['3', '2']
- 遍历对象所有属性进行深拷贝
function cloneObj(obj) {
var newObj = {};
if (obj instanceof Array) {
newObj = [];
}
for (var key in obj) {
var val = obj[key];
newObj[key] = typeof val === 'object' ? cloneObj(val) : val;
}
return newObj;
}
- 使用 JSON 进行序列化与反序列化,但深拷贝对象中不能有函数
function cloneObj(obj) {
return JSON.parse(JSON.stringify(obj));
}
function isJSON(str) {
if (typeof str == 'string') {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
}
- 指定对象开启全屏模式
function launchFullscreen(element) {
if (element.requestFullscreen) {
// Default
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
// FireFox 10+
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
// Google Chrome 15+, Microsoft Edge, Opera 15+, Safari 5.1+
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
// Internet Explorer 11
element.msRequestFullscreen();
} else {
console.log('不支持全屏API');
}
}
- 退出全屏模式
function exitFullscreen() {
if (document.exitFullscreen) {
// Default
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
// FireFox 10+
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
// Google Chrome 15+, Microsoft Edge, Opera 15+, Safari 5.1+
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
// Internet Explorer 11
document.msExitFullscreen();
} else {
console.log('不支持全屏API');
}
}
- 全屏模式改变事件
function fullscreenChange() {
document.addEventListener('fullscreenchange', function(e) {
console.log('fullscreenchange');
console.log(e);
});
document.addEventListener('mozfullscreenchange', function(e) {
console.log('mozfullscreenchange');
console.log(e);
});
document.addEventListener('webkitfullscreenchange', function(e) {
console.log('webkitfullscreenchange');
console.log(e);
});
document.addEventListener('msfullscreenchange', function(e) {
console.log('msfullscreenchange');
console.log(e);
});
}
注:基于浏览器安全考虑,全屏模式无法直接调用,必须有用户行为才能调用全屏API。直接调用会提示警告,并调用失败。
- 防抖与节流都是对多次调用的函数做次数限制。例如函数请求后台,调用频率要做限制,或用户输入、键入、滑动、滚动等事件会大量调用回调函数
防抖(debounce): 在指定时间后调用指定函数,若指定时间内调用该函数,将重置计时器直到指定时间后才会调用指定函数。适用于会大量调用回调函数的事件。
/**
* 防抖(debounce)
* @param method {Function} 被防抖函数
* @param delay {Number(ms)} 延迟时间,单位为毫秒
* @param context 执行函数的上下文
*/
function debounce(method, delay, context) {
var args = arguments;
clearTimeout(method.timer);
method.timer = setTimeout(function () {
method.apply(context, args);
}, delay);
}
节流(throttle): 以指定时间为一个周期,调用指定函数,限制调用频率。适用于有后台请求的函数。
/**
* 节流(throttle)
* @param method {Function} 被节流函数
* @param delay {Number(ms)} 延迟时间,单位为毫秒
* @param context 执行函数的上下文
*/
function throttle(method, delay, context){
var args = arguments;
// 标志位
var canRun = true;
return function() {
if (!canRun) {
return;
}
setTimeout(function() {
method.apply(context, args);
canRun = true;
}, delay);
};
}
- JQuery 在 prototype 之后引入,即:
<script src="prototype.js" type="text/javascript"/> <script src="jquery.js" type="text/javascript"/>
// 改变 JQuery 的选择标识符,将 $ 的控制权交还给 Prototype 。 var jq = jQuery.noConflict(); // 使用 JQuery 选择器的方式改为如下: jq("#id").text();
- JQuery 在 prototype 之前引入,即:
<script src="jquery.js" type="text/javascript"/> <script src="prototype.js" type="text/javascript"/>
// 这种情况 $ 为 Prototype 中定义的标识符,要想使用 JQuery 的选择器,需用如下形式: jQuery("#id").text();
- 通用解决方案,不管引入的先后顺序:
// JQuery 放弃 $ 所有权 jQuery.noConflict(); (function($){ ..... //此时在这个语句块中使用的都是 JQuery 中定义的 $ $('#id').text(); })(jQuery)
$.extend({
getUrlVars: function(){
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
},
getUrlVar: function(name){
return $.getUrlVars()[name];
}
});
// 调用方法
$(document).ready(function() {
var args = $.getUrlVars();
var arg1 = $.getUrlVar('argName1');
var arg2 = $.getUrlVar('argName2');
});
$("#id").die().live("click", function() {
// click event
...
);
$("#id").unbind("click").click(function() {
// click event
...
});
- 加载单个 JavaScript 文件
$.ajax({ url : "js file path", dataType : "script", cache : true, success : function () { // 成功加载 js 文件后的回调函数 } }); $.getScript("js file path", function() { // 成功加载 js 文件后的回调函数 });
- 加载多个 Javascript 文件
$.when( $.getScript("js file path 1"), $.getScript("js file path 2"), $.getScript("js file path 3") ).done(function() { // 全部 js 文件加载后的回调函数 });
<form id="test" method="post" action="/form/submit" >
<input type="text" name="username" id="username" />
<input type="password" name="password" id="password" />
<div id="btn">Sumbit</div>
</form>
<script type="text/javascript" src="jquery.min.js" ></script>
<script type="text/javascript" src="jquery.form.min.js" ></script>
<script>
$(function() {
$("#btn").click(function() {
$("#test").ajaxSubmit({
beforeSubmit: function(arr, $form, options) { // 提交表单前的回调函数
// arr: 表单数据
// $form: 表单对象
// options: 表单提交的可选对象
// 回调函数返回 false 表单将不会提交
console.log(arr);
},
url: '/form/sumbit', // 表单提交的链接
dataType: 'json', // 预期响应的数据类型
data: { // 和表单一起提交的额外数据对象
sumbitType: 'save',
otherDate: 'otherDate'
},
type: 'post', // 提交表单的类型(GET, POST, PUT)
clearForm: true, // 提交表单成功后是否清除表单中的数据
uploadProgress: function(event, position, total, percentComplete) {
// event: 事件对象
// position: 上传的位置
// total: 上传的总量
// percentComplete: 完成的百分数
console.log(position, total, percentComplete + "%");
},
success: function(responseText) { // 提交成功后的回调函数
alert(responseText);
},
error: function () { //提交错误后的回调函数
alert("error");
}
});
});
});
</script>
// 选择框中选中文本的下标
var idx = $(id).selectedIndex;
// 获取文本
var text = $(id).options[idx].text.strip();
- 运行下面的 .bashrc_docker 文件
# .bashrc_docker
alias docker-pid="sudo docker inspect --format '{{.State.Pid}}'"
alias docker-ip="sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}'"
# the implementation refs from https://github.com/jpetazzo/nsenter/blob/master/docker-enter
function docker-enter() {
#if [ -e $(dirname "$0")/nsenter ]; then
#Change for centos bash running
if [ -e $(dirname '$0')/nsenter ]; then
# with boot2docker, nsenter is not in the PATH but it is in the same folder
NSENTER=$(dirname "$0")/nsenter
else
# if nsenter has already been installed with path notified, here will be clarified
NSENTER=$(which nsenter)
#NSENTER=nsenter
fi
[ -z "$NSENTER" ] && echo "WARN Cannot find nsenter" && return
if [ -z "$1" ]; then
echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]"
echo ""
echo "Enters the Docker CONTAINER and executes the specified COMMAND."
echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
else
PID=$(sudo docker inspect --format "{{.State.Pid}}" "$1")
if [ -z "$PID" ]; then
echo "WARN Cannot find the given container"
return
fi
shift
OPTS="--target $PID --mount --uts --ipc --net --pid"
if [ -z "$1" ]; then
# No command given.
# Use su to clear all host environment variables except for TERM,
# initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH,
# and start a login shell.
#sudo $NSENTER "$OPTS" su - root
sudo $NSENTER --target $PID --mount --uts --ipc --net --pid su - root
else
# Use env to clear all host environment variables.
sudo $NSENTER --target $PID --mount --uts --ipc --net --pid env -i $@
fi
fi
}
- 进入指定 Docker 容器
$ docker ps # 查看容器的名称或者 ID $ docker-pid <containerId> # 查看容器的 PID $ docker-enter <containerId> # 进入指定容器
# 删除所有停止的容器,防止删除镜像时应有依赖的容器存在,而导致删除失败 $ docker rm $(docker ps -a | grep "Exited" | awk '{print $1}') # 删除所有 <none> 镜像 $ docker rmi $(docker images | grep "<none>" | awk '{print $3}')
- 缺少 libncurses-dev 包
$ yum install libncurses-dev -y 或 $ apt-get install libncurses-dev
轻量级标记语言是一种 语法简单 的标记语言。它使用易于理解的格式标记,没有古怪的<标签>。 md为Markdown,gfm是GitHub风格的Markdown,rst为reStructedText,ttl为Textile,asc为AsciiDoc,org为Org-mode
- Vim 默认查询是区分大小写的,忽略大小写的配置
// 忽略大小写查询 : set ignorecase
- 智能忽略大小写,输入为全小写时,忽略大小写进行查询,当输入中有至少一个大写时,则进行大小写敏感查询
// 智能忽略大小写 // 模式 匹配 // vim vim,Vim,vIm,viM,VIm,ViM,vIM,VIM // Vim Vim // VIm VIm : set ignorecase smartcase
- 初始化 $ git init - clone别人的库 $ git clone ssh://user@domain.com/repo.git - 查看库的状态 $ git status - 查看工作区与暂存区文件的修改 $ git diff - 添加文件到暂存区 $ git add . - 提交文件到本地库 $ git commit - 提交历史纪录 $ git log - 查看库的分支 $ git branch - 切换分支 $ git checkout <branch> - 将本地库推送至远程库中 $ git push <remote> <branch> - 将指定分支合并至当前分支 $ git merge <branch>
- 清理暂存区的文件 $ git rm -r --cached . - 添加所有文件 $ git add . - 提交 $ git commit -m ".gitignore is now working"
$ git reset --soft HEAD^
- 修改 .gitconfig 文件中[alias]属性
$ vi ~/.gitconfig [alias] st = status ci = commit co = checkout br = branch
- 使用 git config –global alias.[alias-name] [operation-name]
# 表示将 st 作为 status 的别名,可以直接使用 git st 命令 $ git config --global alias.st status
- 首次推送,添加远程代码库至配置 $ git remote add tip https://github.com/Saisimon/tip.git - 推送代码至远程代码库 $ git push tip master - 输入账号密码进行确认
- 暂存工作区 $ git stash - 拉取远程代码 $ git pull origin master - 还原最近一次工作区的内容 $ git stash pop - 出现冲突时,解决冲突提交即可
- 检查是否存在 .ssh 文件夹 $ cd ~/.ssh - 生成 ssh_key $ ssh-keygen -t rsa -C "youremail@email.com" - 输入密码 - 启动 ssh-agent $ eval "$(ssh-agent -s)" - 添加 ssh_key 到 ssh-agent $ ssh-add ~/.ssh/id_rsa - 将 id_rsa.pub 中的key添加进 Github 中 github >> Settings >> SSH and GPG keys >> new SSH key - 测试联通性 $ ssh git@github.com Hi Saisimon! You've successfully authenticated, but GitHub does not provide shell access. Connection to github.com closed.
- 修改 git config 属性
$ git config --global core.quotepath false
- 修改修改 git 配置文件
$ vi ~/.gitconfig [core] quotepath = false
- 修改 remote 地址
$ git remote set-url origin <ssh url>
- 查看 remote 地址
$ git remote -v
- 开机自动开启 GitLab 服务
$ systemctl enable gitlab-runsvdir.service $ systemctl start gitlab-runsvdir.service
- 关闭开机自启动 GitLab 服务
$ systemctl disable gitlab-runsvdir.service $ systemctl stop gitlab-runsvdir.service
- 批量修改提交作者信息脚本,替换 [xxx] 为对应值
#!/bin/sh git filter-branch --env-filter ' an="$GIT_AUTHOR_NAME" am="$GIT_AUTHOR_EMAIL" cn="$GIT_COMMITTER_NAME" cm="$GIT_COMMITTER_EMAIL" if [ "$GIT_COMMITTER_EMAIL" = "[旧邮箱地址]" ] then cn="[新作者名称]" cm="[新邮箱地址]" fi if [ "$GIT_AUTHOR_EMAIL" = "[旧邮箱地址]" ] then an="[新作者名称]" am="[新邮箱地址]" fi export GIT_AUTHOR_NAME="$an" export GIT_AUTHOR_EMAIL="$am" export GIT_COMMITTER_NAME="$cn" export GIT_COMMITTER_EMAIL="$cm" '
- 在待修改的 Git 仓库运行以上脚本
- 推送修改后的 Git 仓库
git push --force --tags origin 'refs/heads/*'
# 切换到 merge 操作时的分支 $ git checkout [分支名] # 回退到 merge 前的版本 $ git reset --hard [merge 前的版本号] # 撤回暂存区提交的修改文件 $ git reset HEAD . # 还原所有被追踪的修改文件 $ git checkout . # 确定需要删除的未被追踪的新建文件与目录 $ git clean -ndf # 删除所有未被追踪的新建文件与目录 $ git clean -df